#include <sxMSS_ReplayEditController.h>

Sx::Simulation::Interface::MSS_ReplayEditController::MSS_ReplayEditController(Viewport* viewport, const std::shared_ptr<Canvas>& canvas, const std::shared_ptr<ScenePickingController>& pickingController, const std::shared_ptr<RendererImplementation>& cameraRenderer, const std::shared_ptr<MassSpringSimulation>& simulation, const std::shared_ptr<MassSpringSimulationRecording>& recording, const std::shared_ptr<MassSpringSimulationRecorder>& recorder, const std::shared_ptr<RendererImplementation>& previewMeshRenderer) :
	MassSpringSimulationReplayController(viewport, canvas, pickingController, cameraRenderer, simulation, recording), recorder(nullptr), previewRecording(nullptr), previewRecorder(nullptr) {
	
	this->recorder = recorder;
	this->previewMeshRenderer = previewMeshRenderer;
	this->previewRecording = std::make_shared<MSS_PreviewRecording>();
	this->previewRecorder = std::make_shared<MSS_PreviewRecorder>(this->simulation, this->previewRecording);
}

Sx::Simulation::Interface::MSS_ReplayEditController::~MSS_ReplayEditController() {}

void Sx::Simulation::Interface::MSS_ReplayEditController::update(float dt) {
	bool isRecording = this->recorder->isRecording();

	if ( isRecording == true ) {
		if ( this->frameIndex >= this->forceStartIndex && this->frameIndex <= this->forceEndIndex ) this->applyMeshForce();
		if ( this->frameIndex >= this->forceEndIndex ) this->removeMeshForce();

		this->simulation->resume();
		this->simulation->update();
	}
	// NOT RECORDING
	else {
		if ( this->paused == false ) {
			bool validFrameLoaded = this->setCurrentFrame(this->frameIndex);


			// not recording.. no more frames.. then allow the mass spring sim to take over.
			if ( validFrameLoaded == false ) {
				// could continue recording here..
				// apply stored external force to mesh..

				if ( this->frameIndex >= this->forceStartIndex && this->frameIndex <= this->forceEndIndex ) this->applyMeshForce();
				if ( this->frameIndex >= this->forceEndIndex ) this->removeMeshForce();
				// if no frames we COULD just continue..
				this->recorder->setRecording(true);

				this->simulation->resume();
				this->simulation->update();
				//this->simulation->pause();

				// since the sim continues.. update the timelines..
				for ( unsigned int i = 0; i < this->timelines.size(); i++ ) {
					if ( this->timelines[i] == nullptr ) continue;
					this->timelines[i]->setEndTime(this->frameIndex);
					this->timelines[i]->setCurrentTime(this->frameIndex);
				}
			}
		}
	}
}

void Sx::Simulation::Interface::MSS_ReplayEditController::render() const {
	// Render the simulation.
	MassSpringSimulationReplayController::render();
	
	for ( unsigned int i = 0; i < this->previewMeshes.size(); i++ ) {
		this->previewMeshes[i].render();
	}
}

bool Sx::Simulation::Interface::MSS_ReplayEditController::applyExternalForce(const std::shared_ptr<FixedTimelineCurveController>& curveEditor) {
	if ( curveEditor == nullptr ) return false;
	if ( this->recording == nullptr ) return false;

	// Need to get the starting position (timeline index) of the force curve.
	this->forceStartIndex = curveEditor->getEndPointSnapPositionX(false);
	this->forceEndIndex = curveEditor->getEndPointSnapPositionX(true);

	std::cout << "Curve Start Position: " << forceStartIndex << std::endl;
	std::cout << "Curve End Position: " << forceEndIndex << std::endl;

	// Use the starting-time index of the curve to invalidate the recording
	// past that point (control will return to the mass-spring simulation).
	this->recording->invalidateGreaterThan(forceStartIndex);
	curveEditor->getCurveApproximation(this->points);

	return true;
}

void Sx::Simulation::Interface::MSS_ReplayEditController::removeMeshForce() {
	// if no longer applying force then zero it out for all nodes.
	std::shared_ptr<Objects::MassSpringMesh> mesh = this->simulation->getMesh(1);
	Eigen::Vector3d zero = Eigen::Vector3d::Zero();
	for ( unsigned int i = 0; i < mesh->getNodeCount(); i++ ) {
		mesh->setNodeExternalForce(i, zero);
	}
}

void Sx::Simulation::Interface::MSS_ReplayEditController::applyMeshForce() {
	unsigned int index =  this->frameIndex - this->forceStartIndex;
	float forceMagnitude = this->points[index].y();
	this->simulation->zeroAllCollisions(); // yeah so this is important..
	Eigen::Vector3d externalForce = Eigen::Vector3d::UnitY();
	externalForce *= forceMagnitude;
	
	// hard coded!!!
	std::shared_ptr<Objects::MassSpringMesh> mesh = this->simulation->getMesh(1);
	

	for ( unsigned int i = 0; i < mesh->getNodeCount(); i++ ) {
		mesh->setNodeExternalForce(i, externalForce);
	}
}

bool Sx::Simulation::Interface::MSS_ReplayEditController::generateSimulationPreview(unsigned int n, unsigned int m) {
	if ( n == 0 || m == 0 ) return false;

	// turn off the main recorder so it doesn't log the preview
	this->recorder->setRecording(false);

	this->previewRecorder->setRecording(true);
	this->simulation->resume();
	// this needs the force curve data..
	this->previewRecorder->recordSteps(n, m);

	//--------------------------------------------------------------------------
	// This will render each mesh as many times as there are recorded frames in
	// the previewRecording.
	//--------------------------------------------------------------------------
	MSS_PreviewKeyFrame frame;
	MSM_PreviewState state;
	for ( unsigned int i = 0; i < this->previewRecording->getFrameCount(); i++ ) {
		this->previewRecording->getFrame(i, frame);

		unsigned int meshCount = frame.meshPreviewStates.size();
		for ( unsigned int x = 0; x < meshCount; x++ ) {
			StoredMesh_Archetype previewArchetype(this->previewMeshRenderer);

			frame.meshPreviewStates.get(x, state);
			state.load(previewArchetype);
			
			this->previewMeshes.add(previewArchetype);
		}
	}

	// might be a pain to make this work..
	if ( this->recorder->getRecordingLength() > 0 )
		this->setCurrentFrame(this->recorder->getRecordingLength() - 1);
	this->simulation->pause();
	return true;
}

void Sx::Simulation::Interface::MSS_ReplayEditController::clearSimluationPreview() {
	this->previewMeshes.clear();
	this->previewRecording->clear();
}
